/*
 * Copyright (C) 2006-2010 - Frictional Games
 *
 * This file is part of HPL1 Engine.
 *
 * HPL1 Engine is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * HPL1 Engine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with HPL1 Engine.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "impl/MeshLoaderCollada.h"

#include "system/LowLevelSystem.h"
#include "graphics/LowLevelGraphics.h"
#include "graphics/VertexBuffer.h"
#include "system/String.h"
#include "system/System.h"

#include "scene/Scene.h"
#include "scene/PortalContainer.h"
#include "scene/World3D.h"
#include "scene/MeshEntity.h"
#include "scene/Light3DPoint.h"
#include "scene/Light3DSpot.h"
#include "scene/Node3D.h"
#include "scene/ColliderEntity.h"
#include "scene/SoundEntity.h"

#include "graphics/Mesh.h"
#include "graphics/SubMesh.h"
#include "graphics/Material.h"

#include "resources/MaterialManager.h"
#include "resources/MeshManager.h"
#include "resources/Resources.h"
#include "resources/FileSearcher.h"

#include "graphics/Bone.h"
#include "graphics/Skeleton.h"
#include "graphics/Animation.h"
#include "graphics/AnimationTrack.h"

#include "graphics/BillBoard.h"
#include "graphics/Beam.h"
#include "graphics/ParticleSystem3D.h"

#include "physics/Physics.h"
#include "physics/PhysicsWorld.h"
#include "physics/PhysicsBody.h"
#include "physics/PhysicsMaterial.h"
#include "physics/SurfaceData.h"

#include "haptic/Haptic.h"
#include "haptic/HapticShape.h"
#include "haptic/HapticSurface.h"
#include "haptic/LowLevelHaptic.h"

#include "impl/tinyXML/tinyxml.h"

#include "math/Math.h"

namespace hpl {

#define GetAdress(sStr) if(sStr[0]=='#') sStr = cString::Sub(sStr,1);

	//////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS
	//////////////////////////////////////////////////////////////////////////

	//-----------------------------------------------------------------------

	cMeshLoaderCollada::cMeshLoaderCollada(iLowLevelGraphics *apLowLevelGraphics)
		: iMeshLoader(apLowLevelGraphics)
	{
		mFlags =0;
	}

	//-----------------------------------------------------------------------

	cMeshLoaderCollada::~cMeshLoaderCollada()
	{
	}

	//-----------------------------------------------------------------------

	//////////////////////////////////////////////////////////////////////////
	// PUBLIC METHODS
	//////////////////////////////////////////////////////////////////////////

	//-----------------------------------------------------------------------

	cWorld3D* cMeshLoaderCollada::LoadWorld(const tString& asFile, cScene* apScene, tWorldLoadFlag aFlags)
	{
		//Images
		tColladaImageVec vColladaImages;
		//Textures
		tColladaTextureVec vColladaTextures;
		//Materials
		tColladaMaterialVec vColladaMaterials;
		//Geometries
		tColladaGeometryVec vColladaGeometries;
		//Lights
		tColladaLightVec vColladaLights;
		//Scene
		cColladaScene ColladaScene;

		mFlags = aFlags;

		unsigned long lStartTime = GetApplicationTime();

		//Fill the structures with collada file data
		bool bRet = FillStructures(asFile, &vColladaImages, &vColladaTextures,
			&vColladaMaterials,&vColladaLights,
			&vColladaGeometries,
			NULL,NULL,
			&ColladaScene,true);

		unsigned long lTime = GetApplicationTime() - lStartTime;
		Log("Loading collada for '%s' took: %d ms\n",asFile.c_str(),lTime);

		if(bRet==false) return NULL;

		cWorld3D *pWorld = apScene->CreateWorld3D(cString::SetFileExt(cString::GetFileName(asFile),""));
		pWorld->SetFileName(cString::GetFileName(asFile));

		cPortalContainer *pPortalContainer = pWorld->GetPortalContainer();

		/////////////////////////////////////
		//Create the physics world
		iPhysicsWorld *pPhysiscsWorld = pWorld->GetPhysics()->CreateWorld(true);
		pWorld->SetPhysicsWorld(pPhysiscsWorld,true);

		//////////////////////////////////////////////////////////////
		//Find the rooms and and add them as sectors in the world.
		//Log("FIND ROOMS:\n");
		tColladaNodeListIt it = ColladaScene.mlstNodes.begin();
		for(; it != ColladaScene.mlstNodes.end(); it++)
		{
			cColladaNode* pNode = *it;

			//Only need to check if it is a special type
			if(pNode->msName[0] == '_')
			{
				//Get 5 first letters and check if these match "_room"
				tString sType = cString::Sub(pNode->msName,0,5);
				//Log("Type: %s\n",sType.c_str());
				if(cString::ToLowerCase(sType) == "_room")
				{
					//Get Room number
					tString sNum = cString::Sub(pNode->msName,5);
					//int lNum = cString::ToInt(sNum.c_str(),-1);
					tString sRoomId = sNum;

					//Log("Adding room num %s\n",sRoomId.c_str());

					//Add sector
					pPortalContainer->AddSector(sRoomId);

					//Add all childs of this node to the room.
					tColladaNodeListIt ChildIt = pNode->mlstChildren.begin();
					for(; ChildIt != pNode->mlstChildren.end();ChildIt++)
					{
						AddSectorChildren(*ChildIt,sRoomId, pWorld,vColladaGeometries,vColladaLights,
											vColladaMaterials,vColladaTextures,vColladaImages);
					}
				}
			}
		}

		//////////////////////////////////////////////////////////////
		//Iterate the nodes and add remaining objects to the scene.
		//Log("ADD OBJECTS:\n");
		it = ColladaScene.mRoot.mlstChildren.begin();
		for(; it != ColladaScene.mRoot.mlstChildren.end(); it++)
		{
			AddSceneObjects(*it, pWorld, vColladaGeometries,vColladaLights,
				vColladaMaterials,vColladaTextures,vColladaImages,&ColladaScene);
		}

		pWorld->SetUpData();

		return pWorld;
	}

	//-----------------------------------------------------------------------

	static cColladaNode* GetNodeFromController(const tString& asGeomId,
												tColladaControllerVec &avColladaControllers,
												cColladaScene &aColladaScene)
	{
		tString sControlId="";
		bool bGuess=false;
		for(int ctrl=0; ctrl < (int)avColladaControllers.size(); ctrl++)
		{
			cColladaController &Control = avColladaControllers[ctrl];
			if(Control.msTarget == asGeomId){
				sControlId = Control.msId;
				bGuess=false;
			}
			//Guessing, if no controller found try the one with source "".
			else if(sControlId=="" && Control.msTarget=="") {
				sControlId = Control.msId;
				bGuess = true;
			}
		}

		if(bGuess) Warning("No controller for for geometry %s, guessing on %s target = ''\n",
							asGeomId.c_str(), sControlId.c_str());

		if(sControlId==""){
			Warning("No controller refered to the geometry!\n");
			return NULL;
		}

		cColladaNode* pNode = aColladaScene.GetNodeFromSource(sControlId);
		if(pNode==NULL){
			Warning("No node for controller '%s'\n",sControlId.c_str());
		}

		return pNode;
	}

	//-------------------------------------------------

	static void FixLocalTransform(cMatrixf *apMatrix, cColladaNode *apNode,
								tColladaAnimationVec &avColladaAnimations, cSkeleton *apSkeleton, bool abHasSeveralBodies)
	{
		cColladaNode *pParentNode = apNode->pParent;
		if(pParentNode && apSkeleton==NULL)
		{
			if(avColladaAnimations.empty() == false || abHasSeveralBodies)
			{
				*apMatrix = cMath::MatrixMul(cMath::MatrixScale(pParentNode->mvScale),*apMatrix);
			}
			else
			{
				*apMatrix = cMath::MatrixMul(pParentNode->m_mtxTransform,*apMatrix);
			}
		}
	}

	//-------------------------------------------------

	static void FixLocalPosition(cVector3f *apPos, cColladaNode *apNode,
		tColladaAnimationVec &avColladaAnimations, cSkeleton *apSkeleton, bool abHasSeveralBodies)
	{
		cColladaNode *pParentNode = apNode->pParent;
		if(pParentNode && apSkeleton==NULL)
		{
			if(avColladaAnimations.empty() == false || abHasSeveralBodies)
			{
				*apPos = cMath::MatrixMul(cMath::MatrixScale(pParentNode->mvScale),*apPos);
			}
			else
			{
				*apPos = cMath::MatrixMul(pParentNode->m_mtxTransform,*apPos);
			}
		}
	}

	//-------------------------------------------------

	cMesh* cMeshLoaderCollada::LoadMesh(const tString& asFile,tMeshLoadFlag aFlags)
	{
		/////////////////////////////////////////////////
		// SETUP TEMP DATA STRUCTURES
		//Images
		tColladaImageVec vColladaImages;
		//Textures
		tColladaTextureVec vColladaTextures;
		//Materials
		tColladaMaterialVec vColladaMaterials;
		//Lights
		tColladaLightVec vColladaLights;
		//Geometries
		tColladaGeometryVec vColladaGeometries;
		//Controllers
		tColladaControllerVec vColladaControllers;
		//Animations
		tColladaAnimationVec vColladaAnimations;
		//Scene
		cColladaScene ColladaScene;

		mFlags = aFlags;

		//Fill the structures with collada file data
		tColladaGeometryVec *pGeomVec = (aFlags & eMeshLoadFlag_NoGeometry) ? NULL : &vColladaGeometries;

		bool bRet = FillStructures(asFile, &vColladaImages, &vColladaTextures,
									&vColladaMaterials,&vColladaLights,
									pGeomVec, &vColladaControllers,
									&vColladaAnimations,
									&ColladaScene,true);

		if(bRet==false) return NULL;

		////////////////////////
		//Create Skeleton
		cSkeleton * pSkeleton = NULL;
		if(vColladaControllers.empty() == false)
		{
			pSkeleton = hplNew( cSkeleton, () );

			tColladaNodeListIt it = ColladaScene.mRoot.mlstChildren.begin();
			for(; it != ColladaScene.mRoot.mlstChildren.end(); it++)
			{
				cColladaNode *pNode = *it;

				CreateSkeletonBone(pNode, pSkeleton->GetRootBone());
			}

			////////////////////////////////////
			//Set the bind position of the bones
			//(This will set the local matrix as the global bind)
			for(size_t i=0; i< vColladaControllers.size();i++)
			{
				cColladaController &Ctrl = vColladaControllers[i];

				for(size_t j=0; j<Ctrl.mvJoints.size(); j++)
				{
					cBone *pBone = pSkeleton->GetBoneByName(Ctrl.mvJoints[j]);

					if(pBone)
					{
						pBone->SetTransform(cMath::MatrixInverse(Ctrl.mvMatrices[j]));
						pBone->SetValue(1);
					}
					else
					{
						Log("Bone '%s' does not exist\n",Ctrl.mvJoints[j].c_str());
					}
				}
			}

			////////////////////////////////////////////////
			//Do another pass and calculate the local matrix
			cBoneIterator BoneIt = pSkeleton->GetRootBone()->GetChildIterator();
			while(BoneIt.HasNext())
			{
				cMatrixf mtxRoot = cMatrixf::Identity;
				CalcLocalMatrixRec(BoneIt.Next(), mtxRoot,0);
			}
		}

		////////////////////////////////////
		//Check for joints or several bodies.
		bool bHasSeveralBodies = false;

		tString sColliderGroup="";
		bool bFoundCollider=false;
		tColladaNodeListIt nodeIt = ColladaScene.mlstNodes.begin();
		for(; nodeIt != ColladaScene.mlstNodes.end(); ++nodeIt)
		{
			cColladaNode *pNode = *nodeIt;

			//Check if it is a joint
			if(cString::ToLowerCase(cString::Sub(pNode->msName,0,6)) == "_joint")
			{
				bHasSeveralBodies = true;
				break;
			}
			//Check if it is a collider. In that case check if this is a new group name.
			if(cString::ToLowerCase(cString::Sub(pNode->msName,0,9)) == "_collider")
			{
				tString sGroup ="";
				if(pNode->pParent) sGroup = pNode->pParent->msName;

				if(bFoundCollider==false)
				{
					bFoundCollider = true;
					sColliderGroup = sGroup;
				}
				else if(sGroup != sColliderGroup)
				{
					bHasSeveralBodies = true;

					break;
				}
			}
		}


		////////////////////////////////////
		//Create Mesh
		tString sMeshName = cString::GetFileName(asFile);
		cMesh *pMesh = hplNew( cMesh, (sMeshName,mpMaterialManager,mpAnimationManager) );

		//Set the skeleton to the mesh
		if(pSkeleton) pMesh->SetSkeleton(pSkeleton);

		//Create Sub meshes
		for(int i=0;i<(int)vColladaGeometries.size(); i++)
		{
			cColladaGeometry &Geom = vColladaGeometries[i];

			cColladaNode *pGeomNode = ColladaScene.GetNodeFromSource(Geom.msId);
			if(pGeomNode==NULL)
			{
				pGeomNode = GetNodeFromController(Geom.msId,vColladaControllers,ColladaScene);
				if(pGeomNode==NULL){
					Error("No node with geometry id '%s'\n",Geom.msId.c_str());
					continue;
				}
			}
			tString sNodeName = pGeomNode->msName;

			/////////////////////////////////////////////////////
			//If the name starts with '_' it is a special object.
			if(sNodeName[0] == '_')
			{
				tStringVec vStrings;
				tString sSepp = "_";
				cString::GetStringVec(sNodeName,vStrings,&sSepp);


				/////////////////////////////////////
				//JOINT
				if(cString::ToLowerCase(vStrings[0]) == "joint" && vStrings.size()>1)
				{
					tFloatVec vVertexVec;
					tVertexVec &vArray = Geom.mvVertexVec;
					vVertexVec.resize(vArray.size() *3);

					for(size_t vtx=0; vtx < vArray.size(); ++vtx)
					{
						vVertexVec[vtx*3 + 0] = vArray[vtx].pos.x;
						vVertexVec[vtx*3 + 1] = vArray[vtx].pos.y;
						vVertexVec[vtx*3 + 2] = vArray[vtx].pos.z;
					}

					cBoundingVolume TempBV;
					TempBV.AddArrayPoints(&vVertexVec[0],(int)vArray.size());
					TempBV.CreateFromPoints(3);

					cColladaNode *pNode = ColladaScene.GetNodeFromSource(Geom.msId);
					if(pNode==NULL){Warning("No node for geometry '%s' found when creating joint!\n",Geom.msId.c_str()); continue;}


					tString sJointType = cString::ToLowerCase(vStrings[1]);
					ePhysicsJointType JointType;
					if(sJointType=="hinge") JointType = ePhysicsJointType_Hinge;
					else if(sJointType=="ball") JointType = ePhysicsJointType_Ball;
					else if(sJointType=="slider") JointType = ePhysicsJointType_Slider;
					else if(sJointType=="screw") JointType = ePhysicsJointType_Screw;

					cMeshJoint *pJoint = pMesh->CreatePhysicsJoint(JointType);

					CreateMeshJoint(pJoint, JointType, TempBV, vStrings, pNode,ColladaScene,vColladaGeometries);
				}

				/////////////////////////////////////
				//COLLIDER
				else if(cString::ToLowerCase(vStrings[0]) == "collider" && vStrings.size()>1)
				{
					tFloatVec vVertexVec;
					tVertexVec &vArray = Geom.mvVertexVec;
					vVertexVec.resize(vArray.size() *3);

					for(size_t vtx=0; vtx < vArray.size(); ++vtx)
					{
						vVertexVec[vtx*3 + 0] = vArray[vtx].pos.x;
						vVertexVec[vtx*3 + 1] = vArray[vtx].pos.y;
						vVertexVec[vtx*3 + 2] = vArray[vtx].pos.z;
					}

					cBoundingVolume TempBV;
					TempBV.AddArrayPoints(&vVertexVec[0],(int)vArray.size());
					TempBV.CreateFromPoints(3);

					tString sShapeType = cString::ToLowerCase(vStrings[1]);
					eCollideShapeType ShapeType = eCollideShapeType_Box;
					cVector3f vShapeSize = TempBV.GetSize();

					if(sShapeType == "box"){
						ShapeType = eCollideShapeType_Box;
					}
					else if(sShapeType == "sphere"){
						ShapeType = eCollideShapeType_Sphere;
						vShapeSize *= cVector3f(0.5f);
					}
					else if(sShapeType == "capsule"){
						ShapeType = eCollideShapeType_Capsule;
						vShapeSize.x *= 0.5;
					}
					else if(sShapeType == "cylinder"){
						ShapeType = eCollideShapeType_Cylinder;
						vShapeSize.x *= 0.5;
					}

					cMeshCollider *pCollider = pMesh->CreateCollider(ShapeType);
					pCollider->mvSize = vShapeSize;

					cColladaNode *pNode = ColladaScene.GetNodeFromSource(Geom.msId);
					if(pNode==NULL){Warning("No node for geometry '%s' when creating collider!\n",Geom.msId.c_str()); continue;}

					//Set the name of the group it is in.
					if(pNode->pParent)
					{
						pCollider->msGroup = pNode->pParent->msName;
					}
					else
					{
						pCollider->msGroup = "";
					}

					//Set offset, some primitives are created with the centre at the bottom,
					//fix this.
					//Check if the centre is not in the middle
					TempBV.SetPosition(pNode->m_mtxWorldTransform.GetTranslation());
					if(TempBV.GetWorldCenter() != pNode->m_mtxWorldTransform.GetTranslation())
					{
						cVector3f vOffset = TempBV.GetWorldCenter() -
											pNode->m_mtxWorldTransform.GetTranslation();
						vOffset = vOffset * pNode->mvScale;
						//Log("Centre is not a correct location! Offset: %s\n",vOffset.ToString().c_str());

						//Local postion add
						/*cMatrixf mtxTrans = cMath::MatrixTranslate(vOffset);
						pCollider->m_mtxOffset = cMath::MatrixMul( pNode->m_mtxWorldTransform,
																	mtxTrans);*/

						//World postion add
						pCollider->m_mtxOffset = pNode->m_mtxWorldTransform;
						cVector3f vRotOffset = cMath::MatrixMul(pCollider->m_mtxOffset.GetRotation(),
																vOffset);
						pCollider->m_mtxOffset.SetTranslation(	pCollider->m_mtxOffset.GetTranslation() +
																vRotOffset);
					}
					else
					{
						pCollider->m_mtxOffset = pNode->m_mtxWorldTransform;
					}


					//Add scale
					pCollider->mvSize = pCollider->mvSize * pNode->mvScale;

					//Log("Collider scale: %s\n",pNode->mvScale.ToString().c_str());
					//Log("Collider size: %s\n",pCollider->mvSize.ToString().c_str());

					//This is to orient the cylinder along y axis instead of x.
					if(ShapeType == eCollideShapeType_Cylinder || ShapeType == eCollideShapeType_Capsule)
					{
						pCollider->m_mtxOffset = cMath::MatrixMul(pCollider->m_mtxOffset,
																cMath::MatrixRotateZ(cMath::ToRad(90))
																);
					}

					//Log("Creating Collider %s, type: %d size: %s\n with matrix: %s\n", Geom.msName.c_str(),(int)ShapeType,
					//									pCollider->mvSize.ToString().c_str(),
					//									pCollider->m_mtxOffset.ToString().c_str());
				}

				///// BILLBOARD /////////////////////////////
				else if(cString::ToLowerCase(vStrings[0])=="bb")
				{
					if(vStrings.size() < 3){
						Error("Too few params in billboard entity '%s'\n",sNodeName.c_str());
					}
					else
					{
						tString sName = vStrings[vStrings.size()-1];
						tString sFile = "";
						for(size_t i2=1; i2<vStrings.size()-1; ++i2)
						{
							sFile += vStrings[i2];
							if(i2 != vStrings.size()-2) sFile+="_";
						}

						cMeshBillboard *pBillboard = pMesh->CreateBillboard();

						pBillboard->msName = sName;

						pBillboard->msParent = GetParentName(pGeomNode,&vColladaGeometries);
						pBillboard->msFile = cString::SetFileExt(sFile,"bnt");
						pBillboard->mvSize = cVector2f(pGeomNode->mvScale.x,pGeomNode->mvScale.y);
						pBillboard->mfOffset = pGeomNode->mvScale.z;
						pBillboard->mvPosition = pGeomNode->m_mtxTransform.GetTranslation();
						pBillboard->mvAxis = cMath::Vector3Normalize(cMath::MatrixInverse(pGeomNode->m_mtxWorldTransform).GetUp());
						FixLocalPosition(&pBillboard->mvPosition,pGeomNode,vColladaAnimations,pSkeleton,bHasSeveralBodies);
					}
				}
				///// BEAM /////////////////////////////
				else if(cString::ToLowerCase(vStrings[0])=="beam")
				{
					if(vStrings.size() < 3){
						Error("Too few params in billboard entity '%s'\n",sNodeName.c_str());
					}
					else
					{
						tString sName = vStrings[vStrings.size()-1];
						tString sFile = "";
						for(size_t i2=1; i2<vStrings.size()-1; ++i2)
						{
							sFile += vStrings[i2];
							if(i2 != vStrings.size()-2) sFile+="_";
						}

						tString sEndName = "_beamend_"+sName;
						cColladaNode *pEndNode = ColladaScene.GetNode(sEndName);

						if(pEndNode)
						{
							cMeshBeam *pBeam = pMesh->CreateBeam();

							pBeam->msName = sName;
							pBeam->msFile = sFile;

							pBeam->mvStartPosition = pGeomNode->m_mtxTransform.GetTranslation();
							pBeam->mvEndPosition = pEndNode->m_mtxTransform.GetTranslation();

							pBeam->msStartParent = GetParentName(pGeomNode,&vColladaGeometries);
							pBeam->msEndParent = GetParentName(pEndNode,&vColladaGeometries);
						}
						else
						{
							Error("Couldn't find beam end '%s'!\n",sEndName.c_str());
						}
					}
				}
				///// PARTICLE SYSTEM /////////////////////////////
				else if(cString::ToLowerCase(vStrings[0])=="ps")
				{
					if(vStrings.size() < 3){
						Error("Too few params in particle system entity '%s'\n",sNodeName.c_str());
					}
					else
					{
						tString sName = vStrings[vStrings.size()-1];
						tString sType = "";
						for(size_t i2=1; i2<vStrings.size()-1; ++i2)
						{
							sType += vStrings[i2];
							if(i2 != vStrings.size()-2) sType+="_";
						}

						cMeshParticleSystem *pPS = pMesh->CreateParticleSystem();

						pPS->msName = sName;
						pPS->msParent = GetParentName(pGeomNode,&vColladaGeometries);
						pPS->msType = sType;
						pPS->mvSize = pGeomNode->mvScale;
						pPS->m_mtxTransform = pGeomNode->m_mtxTransform;
						FixLocalTransform(&pPS->m_mtxTransform,pGeomNode,vColladaAnimations,pSkeleton,bHasSeveralBodies);
					}
				}
				///// REFERENCE /////////////////////////////
				else if(cString::ToLowerCase(vStrings[0])=="ref")
				{
					if(vStrings.size() < 3){
						Error("Too few params in ref entity '%s'\n",sNodeName.c_str());
					}
					else
					{
						tString sFile = "";
						for(size_t i2=1; i2<vStrings.size()-1; ++i2)
						{
							sFile += vStrings[i2];
							if(i2 != vStrings.size()-2) sFile+="_";
						}
						tString sName = vStrings[vStrings.size()-1];

						cMeshReference *pRef = pMesh->CreateReference();

						pRef->msName = sName;
						pRef->msFile = sFile;
						pRef->msParent = GetParentName(pGeomNode,&vColladaGeometries);
						pRef->m_mtxTransform = pGeomNode->m_mtxTransform;
						FixLocalTransform(&pRef->m_mtxTransform,pGeomNode,vColladaAnimations,pSkeleton,bHasSeveralBodies);
					}
				}
				///// SOUND ENTITY /////////////////////////////
				else if(cString::ToLowerCase(vStrings[0])=="sound")
				{
					if(vStrings.size() < 3){
						Error("Too few params in sound entity '%s'\n",sNodeName.c_str());
					}
					else
					{
						tString sType = "";
						for(size_t i2=1; i2<vStrings.size()-1; ++i2)
						{
							sType += vStrings[i2];
							if(i2 != vStrings.size()-2) sType+="_";
						}
						tString sName = vStrings[vStrings.size()-1];

						cMeshSoundEntity *pSound = pMesh->CreateSoundEntity();

						pSound->msName = sName;
						pSound->msParent = GetParentName(pGeomNode,&vColladaGeometries);
						pSound->msType = sType;
						pSound->mvPosition = pGeomNode->m_mtxTransform.GetTranslation();
						FixLocalPosition(&pSound->mvPosition,pGeomNode,vColladaAnimations,pSkeleton,bHasSeveralBodies);
					}
				}

				continue;
			}

			tString sSubMeshName = Geom.msName=="" ? Geom.msId : Geom.msName;
			cSubMesh* pSubMesh = pMesh->CreateSubMesh(sSubMeshName);

			//Getting group the sub mesh is in
			cColladaNode *pNode = ColladaScene.GetNodeFromSource(Geom.msId);
			if(pNode==NULL)
			{
				pNode = GetNodeFromController(Geom.msId,vColladaControllers,ColladaScene);
				if(pNode==NULL){
					Warning("No node for geometry '%s' found when trying to create sub mesh, check in controllers.\n",Geom.msId.c_str());
					continue;
				}
			}

			pSubMesh->SetNodeName(pNode->msName);
			pSubMesh->SetLocalTransform(pNode->m_mtxTransform);
			if(pNode->pParent)
			{
				pSubMesh->SetGroup(pNode->pParent->msName);
			}

			//Set the scale
			pSubMesh->SetModelScale(pNode->mvScale);

			//Log("Creating submesh: '%s'\n",Geom.msName.c_str());

			//To be filled by extra vertex positions (not used, use the one in Geometry)
			tColladaExtraVtxListVec &vExtraVtxVec = Geom.mvExtraVtxVec;

			//////////////////////////////
			// Create Vertex buffer
			eVertexBufferUsageType UsageType = eVertexBufferUsageType_Static;
			//The streams will be the entity copies.
			//if(vColladaControllers.empty()==false) UsageType = eVertexBufferUsageType_Stream;

			iVertexBuffer *pVtxBuffer = CreateVertexBuffer(Geom, UsageType);//, vExtraVtxVec);
			pSubMesh->SetVertexBuffer(pVtxBuffer);

			//Create an extra set of vertices for shadow rendering
			pVtxBuffer->CreateShadowDouble(true);

			/////////////////////////////
			//Add material
			tString sMatName = GetMaterialTextureFile(Geom.msMaterial,vColladaMaterials,vColladaTextures,
														vColladaImages);
			//Log("Material name: '%s'\n",sMatName.c_str());
			iMaterial *pMaterial;
			if(mbUseFastMaterial && msFastMaterialFile != "")
			{
				pMaterial = mpMaterialManager->CreateMaterial(msFastMaterialFile);
			}
			else
			{
				pMaterial = mpMaterialManager->CreateMaterial(sMatName);
			}
			if(pMaterial==NULL)
			{
				Error("Couldn't create material '%s' for object '%s'\n",sMatName.c_str(),
																		Geom.msName.c_str());
				continue;
			}
			pSubMesh->SetMaterial(pMaterial);

			/////////////////////////////
			//If there is a controller for the mesh, get vertex-bone pairs.
			//First find the controller.
			cColladaController *pCtrl =NULL;
			for(size_t j=0; j<vColladaControllers.size(); j++){
				if(vColladaControllers[j].msTarget == Geom.msId){
					pCtrl = &vColladaControllers[j];
					break;
				}
			}
			//If no controller can be found, guess the one with target=""
			if(pCtrl==NULL){
				for(size_t j=0; j<vColladaControllers.size(); j++){
					if(vColladaControllers[j].msTarget == ""){
						pCtrl = &vColladaControllers[j];
						Warning("Guessing controller as target=''!\n");
						break;
					}
				}
			}

			//Add the pairs
			if(pCtrl && pSkeleton)
			{
				//Log("Adding vertex-bone pairs!\n");
				//Iterate the pairs
				for(size_t j=0; j<pCtrl->mvPairs.size(); j++)
				{
					//Get all vertices for this vertex pos
					tColladaExtraVtxListIt ExtraIt = vExtraVtxVec[j].begin();
					for(; ExtraIt != vExtraVtxVec[j].end(); ++ExtraIt)
					{
						cColladaExtraVtx &Extra = *ExtraIt;

						//Iterate all the influences for this vertex.
						tColladaJointPairListIt PairIt = pCtrl->mvPairs[j].begin();
						for(; PairIt != pCtrl->mvPairs[j].end(); ++PairIt)
						{
							cColladaJointPair &SrcPair = *PairIt;
							cVertexBonePair DestPair;

							int lBoneIdx = pSkeleton->GetBoneIndexByName(pCtrl->mvJoints[SrcPair.mlJoint]);
							DestPair.boneIdx = lBoneIdx;
							DestPair.weight = pCtrl->mvWeights[SrcPair.mlWeight];
							DestPair.vtxIdx = Extra.mlNewVtx;

							//Add pair in sub mesh
							pSubMesh->AddVertexBonePair(DestPair);

							//Log("Added pair: bone %d vtx %d weight: %f\n", DestPair.boneIdx,DestPair.vtxIdx,
							//												DestPair.weight);
						}
					}
				}

				//Compile the pairs into a more friendly format.
				pSubMesh->CompileBonePairs();

				//Transform the mesh according to the bind transform.
				pVtxBuffer->Transform(pCtrl->m_mtxBindShapeMatrix);
			}
			//////////////////////////////////////////////////
			//No controller, we have a non skinned mesh!
			else
			{
				//We have an animated non-skinned mesh!
				//Need to add the node hierarchy
				if(vColladaAnimations.empty() == false || bHasSeveralBodies)
				{
					//If the buffer has animations or joints, we only add the scale part of the
					//transform
					cMatrixf mtxScale = cMath::MatrixScale(pNode->mvScale);
					pVtxBuffer->Transform(mtxScale);

					//Log("Scaling %s: (%s) %s!\n",pNode->msName.c_str(),pNode->mvScale.ToString().c_str(),mtxScale.ToString().c_str());
				}
				//We have a normal mesh without any geometry and NO joints
				else if(bHasSeveralBodies==false)
				{
					//There are no bones so transform the vertex buffer by the nodes world transform.
					cColladaNode *ptNode = ColladaScene.GetNodeFromSource(Geom.msId);

					if(ptNode)
					{
						pVtxBuffer->Transform(ptNode->m_mtxWorldTransform);
						//Log("Transforming buffer %s\n",pNode->msName.c_str());
					}
					else
					{
						Warning("No node for geometry '%s' found when trying to transform geometry!\n",Geom.msId.c_str());
					}
				}
			}

			//Create triangle data for the geometry
			cMath::CreateTriangleData(*pSubMesh->GetTriangleVecPtr(),
								pVtxBuffer->GetIndices(), pVtxBuffer->GetIndexNum(),
								pVtxBuffer->GetArray(eVertexFlag_Position),
								kvVertexElements[cMath::Log2ToInt(eVertexFlag_Position)],
								pVtxBuffer->GetVertexNum());

			//Create edges for the geometry.
			bool bDoubleSided = false;
			cMath::CreateEdges(*pSubMesh->GetEdgeVecPtr(),
								pVtxBuffer->GetIndices(), pVtxBuffer->GetIndexNum(),
								pVtxBuffer->GetArray(eVertexFlag_Position),
								kvVertexElements[cMath::Log2ToInt(eVertexFlag_Position)],
								pVtxBuffer->GetVertexNum(),
								&bDoubleSided);
			pSubMesh->SetDoubleSided(bDoubleSided);

			pSubMesh->Compile();
		}

		///////////////////////////////////////////////
		// Add Node Hierarchy
		// only add if there are animations and no skeleton.
		if((vColladaAnimations.empty() == false || bHasSeveralBodies) && !pSkeleton)
		{
			cNode3D* pRootNode = pMesh->GetRootNode();

			tColladaNodeListIt it = ColladaScene.mRoot.mlstChildren.begin();
			for(; it != ColladaScene.mRoot.mlstChildren.end(); ++it)
			{
				CreateHierarchyNodes(pMesh, pRootNode,*it,vColladaGeometries);
			}

			//Clear scale on all joint nodes.
			if(bHasSeveralBodies)
			{
				for(int i=0; i<pMesh->GetNodeNum(); i++)
				{
					cNode3D* pMeshNode = pMesh->GetNode(i);
					cMatrixf mtxNode = pMeshNode->GetLocalMatrix();

					cSubMesh *pSubMesh = pMesh->GetSubMeshName(pMeshNode->GetSource());

					if(pSubMesh)
					{
						//Clear the scale
						cMatrixf mtxScale = cMath::MatrixScale(pSubMesh->GetModelScale());
						mtxNode = cMath::MatrixMul(mtxNode, cMath::MatrixInverse(mtxScale));
					}
					else if(tString(pMeshNode->GetSource()) != ""
						&& pMeshNode->GetSource()[0] != '_')
					{
						Error("Cannot find submesh '%s'\n",pMeshNode->GetSource());
					}

					pMeshNode->SetMatrix(mtxNode);
				}
			}
		}

		//Shouldn't be needed any more.
		//vColladaGeometries[i].Clear();

		///////////////////////////////////////////////
		// Create Lights
		for(int i=0; i< (int)vColladaLights.size(); i++)
		{
			////////////////////////////////////////
			//Get the Light and Node
			cColladaLight &ColladaLight = vColladaLights[i];

			cColladaNode *pLightNode = ColladaScene.GetNodeFromSource(ColladaLight.msId);
			if(pLightNode == NULL)
			{
				Error("Couldn't find node for light '%s'\n",ColladaLight.msId.c_str());
				continue;
			}

			tStringVec vParams;
			tString sSepp = "_";
			cString::GetStringVec(pLightNode->msName,vParams,&sSepp);
			tString sLightName = "";
			tString sLightFile ="";


			/////////////////////////////////////
			//Check if there is any light entity file and get name.

			//file parameter
			if((int)vParams.size() >= 2)
			{
				for(size_t i2=0; i2< vParams.size()-1; ++i2)
				{
					sLightFile += vParams[i2];
					if(i2!= vParams.size()-2) sLightFile += "_";
				}
				sLightFile = cString::SetFileExt(sLightFile,"lnt");

				sLightName = vParams[vParams.size()-1];
			}
			//No file parameter
			else
			{
				sLightName = vParams[0];
			}

			cMeshLight *pMeshLight = pMesh->CreateLight(eLight3DType_Point);

			pMeshLight->m_mtxTransform = pLightNode->m_mtxTransform;

			pMeshLight->msParent = GetParentName(pLightNode,&vColladaGeometries);

			pMeshLight->mColor = ColladaLight.mDiffuseColor;
			pMeshLight->mfFOV = cMath::ToRad(ColladaLight.mfAngle);
			pMeshLight->mfRadius = pLightNode->mvScale.x;

			if( ColladaLight.msType == "point")
			{
				pMeshLight->mType = eLight3DType_Point;
				pMeshLight->mColor.a = pLightNode->mvScale.y;
				pMeshLight->mbCastShadows = pLightNode->mvScale.z<0.1f ? false: true;
			}
			else
			{
				pMeshLight->mType = eLight3DType_Spot;
			}

			pMeshLight->msName = sLightName;
			pMeshLight->msFile = sLightFile;

			//////////////////////////////////
			//Fix the transform
			FixLocalTransform(&pMeshLight->m_mtxTransform,pLightNode,vColladaAnimations,pSkeleton,bHasSeveralBodies);
		}

		///////////////////////////////////////////////
		// Create Animations
		cAnimation *pAnimation = NULL;
		if(vColladaAnimations.empty() == false)
		{
			pAnimation = hplNew( cAnimation, (pMesh->GetName(),cString::GetFileName(asFile)) );

			pAnimation->SetAnimationName("Default");
			pAnimation->SetLength(ColladaScene.mfDeltaTime);
			pAnimation->ResizeTracks((int)vColladaAnimations.size());

			pMesh->AddAnimation(pAnimation);

			///////////////////////////////////////////////////
			// Go through all tracks and add them to the animation
			for(size_t i=0; i < vColladaAnimations.size(); i++)
			{
				cAnimationTrack *pTrack = CreateAnimTrack(pAnimation, pSkeleton,
														vColladaAnimations[i],&ColladaScene);
				if(pTrack==NULL) continue;
				if(pSkeleton)
				{
					cBone *pBone = pSkeleton->GetBoneByName(pTrack->GetName());
					int lBoneIdx = pSkeleton->GetBoneIndexByName(pTrack->GetName());
					pTrack->SetNodeIndex(lBoneIdx);
				}
				else {
					//Find sub mesh.
					pTrack->SetNodeIndex(-1);
				}
			}

			/////////////////////////////////////////////////////
			// Go through all bones and check if they have an animation
			// If not create single frame with the node pose.
			if(pSkeleton)
			{
				for(int i=0; i< pSkeleton->GetBoneNum(); ++i)
				{
					cBone *pBone = pSkeleton->GetBoneByIndex(i);

					if(pAnimation->GetTrackByName(pBone->GetName())==NULL)
					{
						cColladaNode *pNode = ColladaScene.GetNode(pBone->GetName());
						if(pNode)
						{
							cMatrixf mtxLocal = pNode->m_mtxTransform;
							cMatrixf mtxInvBone = cMath::MatrixInverse(pBone->GetLocalTransform());

							cMatrixf mtxChange = cMath::MatrixMul(mtxInvBone,mtxLocal);

							//Log("Bone %s change mtx: %s\n",pBone->GetName().c_str(),
							//								mtxChange.ToString().c_str());

							cAnimationTrack * pTrack = pAnimation->CreateTrack(pBone->GetName(),eAnimTransformFlag_Rotate | eAnimTransformFlag_Translate | eAnimTransformFlag_Scale);
							pTrack->SetNodeIndex(i);
							cKeyFrame *pFrame = pTrack->CreateKeyFrame(0);

							pFrame->trans = mtxChange.GetTranslation();
							pFrame->scale = 1;
							pFrame->rotation = cQuaternion::Identity;
							//Get the quaternion from the rotation matrix
							mtxChange.SetTranslation(0);
							pFrame->rotation.FromRotationMatrix(mtxChange);
						}
						else
						{
							Warning("Couldn't find node for bone '%s'\n",pBone->GetName().c_str());
						}
					}
				}
			}
		}


		///////////////////////////////////
		///////// DEBUG ///////////////////
		///////////////////////////////////
		// Test anim data
		/*Log("ANIMATIONS:\n");
		for(size_t i=0; i < vColladaAnimations.size(); i++)
		{
			cColladaAnimation &Anim = vColladaAnimations[i];

			Log("Anim: id: '%s' name: '%s'\n",Anim.msId.c_str(), Anim.msName.c_str());

			Log("Channels:\n");
			for(size_t j=0; j< Anim.mvChannels.size(); j++)
				Log("Id: '%s' Source: '%s' Target: '%s'\n",
																Anim.mvChannels[j].msId.c_str(),
																Anim.mvChannels[j].msSource.c_str(),
																Anim.mvChannels[j].msTarget.c_str());

			Log("Samplers:\n");
			for(size_t j=0; j< Anim.mvSamplers.size(); j++)
				Log("Id: '%s' Target: '%s' Time: '%s' Values: '%s'\n",		Anim.mvSamplers[j].msId.c_str(),
																Anim.mvSamplers[j].msTarget.c_str(),
																Anim.mvSamplers[j].msTimeArray.c_str(),
																Anim.mvSamplers[j].msValueArray.c_str());
			Log("Sources:\n");
			for(size_t j=0; j< Anim.mvSources.size(); j++)
			{
				Log("Id: %s\n",Anim.mvSources[j].msId.c_str());
				for(size_t k=0;k < Anim.mvSources[j].mvValues.size(); k++)
					Log("%f, ",Anim.mvSources[j].mvValues[k]);
				Log("\n");
			}

			//CreateAnimTrack(NULL, Anim,&ColladaScene);
		}*/

		return pMesh;
	}

	//-----------------------------------------------------------------------

	cAnimation* cMeshLoaderCollada::LoadAnimation(const tString& asFile)
	{

		//Controllers
		tColladaControllerVec vColladaControllers;
		//Animations
		tColladaAnimationVec vColladaAnimations;
		//Scene
		cColladaScene ColladaScene;

		//Fill the structures with collada file data
		bool bRet = FillStructures(asFile, NULL, NULL,
									NULL,NULL,
									NULL,&vColladaControllers,
									&vColladaAnimations,
									&ColladaScene,true
									);

		if(bRet==false) return NULL;

		////////////////////////
		//Create Skeleton
		cSkeleton * pSkeleton = NULL;
		if(vColladaControllers.empty() == false)
		{
			pSkeleton = hplNew( cSkeleton, () );

			tColladaNodeListIt it = ColladaScene.mRoot.mlstChildren.begin();
			for(; it != ColladaScene.mRoot.mlstChildren.end(); it++)
			{
				cColladaNode *pNode = *it;

				CreateSkeletonBone(pNode, pSkeleton->GetRootBone());
			}

			////////////////////////////////////
			//Set the bind position of the bones
			//(This will set the local matrix as the global bind)
			for(size_t i=0; i< vColladaControllers.size();i++)
			{
				cColladaController &Ctrl = vColladaControllers[i];

				for(size_t j=0; j<Ctrl.mvJoints.size(); j++)
				{
					cBone *pBone = pSkeleton->GetBoneByName(Ctrl.mvJoints[j]);

					if(pBone)
					{
						pBone->SetTransform(cMath::MatrixInverse(Ctrl.mvMatrices[j]));
						pBone->SetValue(1);
					}
					else
					{
						Log("Bone '%s' does not exist\n",Ctrl.mvJoints[j].c_str());
					}
				}
			}

			////////////////////////////////////////////////
			//Do another pass and calculate the local matrix
			cBoneIterator BoneIt = pSkeleton->GetRootBone()->GetChildIterator();
			while(BoneIt.HasNext())
			{
				cMatrixf mtxRoot = cMatrixf::Identity;
				CalcLocalMatrixRec(BoneIt.Next(), mtxRoot,0);
			}
		}


		///////////////////////////////////////////////
		// Create Animations
		cAnimation *pAnimation = NULL;
		if(vColladaAnimations.empty() == false)
		{
			pAnimation = hplNew( cAnimation, ("Default",cString::GetFileName(asFile)) );

			pAnimation->SetLength(ColladaScene.mfDeltaTime);
			pAnimation->ResizeTracks((int)vColladaAnimations.size());

			for(size_t i=0; i < vColladaAnimations.size(); i++)
			{
				cAnimationTrack *pTrack = CreateAnimTrack(pAnimation, pSkeleton,
															vColladaAnimations[i],&ColladaScene);
				if(pTrack==NULL) continue;
				if(pSkeleton)
				{
					cBone *pBone = pSkeleton->GetBoneByName(pTrack->GetName());
					int lBoneIdx = pSkeleton->GetBoneIndexByName(pTrack->GetName());
					pTrack->SetNodeIndex(lBoneIdx);
				}
				else {
					//Find sub mesh.
					pTrack->SetNodeIndex(-1);
				}
			}
		}

		if(pSkeleton) hplDelete(pSkeleton);

		return pAnimation;
	}

	//-----------------------------------------------------------------------

	bool cMeshLoaderCollada::IsSupported(const tString asFileType)
	{
		if(asFileType == "dae") return true;

		return false;
	}

	//-----------------------------------------------------------------------

	void cMeshLoaderCollada::AddSupportedTypes(tStringVec* avFileTypes)
	{
		avFileTypes->push_back("dae");
	}

	//-----------------------------------------------------------------------

	//////////////////////////////////////////////////////////////////////////
	// PRIVATE METHODS
	/////////////////////////////////////////////////////////////////////////

	//-----------------------------------------------------------------------


	tString cMeshLoaderCollada::GetParentName(cColladaNode *apNode, tColladaGeometryVec *apColladaGeometries)
	{
		tString sParent = "";
		if(apNode->pParent)
		{
			sParent = apNode->pParent->msName;
			//If the parent is a geometry the geometry name must be used.
			if(apNode->pParent->msSource != "")
			{
				cColladaGeometry *pGeom = GetGeometry(apNode->pParent->msSource,*apColladaGeometries);
				if(pGeom)
				{
					sParent = pGeom->msName;
				}
			}
		}

		return sParent;
	}

	//-----------------------------------------------------------------------

	static bool HasParam(const tStringVec & avVec, const tString &asParam)
	{
		for(int i=0; i< (int)avVec.size(); i++)
		{
			if(cString::ToLowerCase(avVec[i]) == asParam){
				return true;
			}
		}

		return false;
	}
	//-----------------------------------------------------------------------

	void cMeshLoaderCollada::CreateMeshJoint(cMeshJoint* apJoint,ePhysicsJointType aJointType,
												cBoundingVolume &aBV,
												tStringVec &avStrings,cColladaNode* apNode,
												cColladaScene &aColladaScene,
												tColladaGeometryVec &avColladaVec)
	{

		//////////////////////////////////////////////
		// GENERAL

		if(avStrings.size()<5){
			Error("Joint node '%s' has to few args!\n",apNode->msName.c_str());
		}

		apJoint->msName = avStrings[avStrings.size()-1];

		//////////////////////////////////////
		// Get if the joint bodies should collide
		if(HasParam(avStrings,"nocollide")){
			apJoint->mbCollide = false;
		}
		else {
			apJoint->mbCollide = true;
		}

		//////////////////////////////////////
		//Get parent and child body
		apJoint->mvPivot = apNode->m_mtxWorldTransform.GetTranslation();
		apJoint->mvPinDir = cMath::Vector3Normalize(apNode->m_mtxWorldTransform.GetUp());

		cColladaNode *pCNode = aColladaScene.GetNode(avStrings[2]);
		cColladaNode *pPNode = aColladaScene.GetNode(avStrings[3]);

		if(pCNode) {
			cColladaGeometry* pGeom = GetGeometry(pCNode->msSource,avColladaVec);
			if(pGeom){
				apJoint->msChildBody =pGeom->msName;
			}
			else{
				Warning("Geometry for joint child '%s' is missing! Might be connected to bone.\n",pCNode->msSource.c_str());
				apJoint->msChildBody = pCNode->msName;
			}
		}
		else
		{
			Error("Child node '%s' is missing for joint '%s'!\n",avStrings[2].c_str(),
							apJoint->msName.c_str());
			return;
		}

		if(pPNode) {
			cColladaGeometry* pGeom = GetGeometry(pPNode->msSource,avColladaVec);
			if(pGeom){
				apJoint->msParentBody =pGeom->msName;
			}
			else{
				Warning("Geometry  joint parten '%s' is missing! Might be connected to bone.\n",pPNode->msSource.c_str());
				apJoint->msParentBody = pPNode->msName;
			}
		}


		//////////////////////////////////////
		//Get min and max values.

		if((aJointType == ePhysicsJointType_Slider || aJointType== ePhysicsJointType_Screw)
			&& avStrings.size() <= 6)
		{
			apJoint->mfMax = aBV.GetMax().y;
			apJoint->mfMin = aBV.GetMin().y;

			if(apJoint->mfMin>0) apJoint->mfMin =0;
			if(apJoint->mfMax<0) apJoint->mfMax =0;

			//Log("Min: %f Max: %f\n",apJoint->mfMin,apJoint->mfMax);
		}
		else
		{
			if(avStrings.size()>=7)
			{
				apJoint->mfMin = cString::ToFloat(avStrings[4].c_str(),0);
				apJoint->mfMax = cString::ToFloat(avStrings[5].c_str(),0);

				if(aJointType == ePhysicsJointType_Slider || aJointType== ePhysicsJointType_Screw)
				{
					apJoint->mfMin = -apJoint->mfMin/100.0f;
					apJoint->mfMax = apJoint->mfMax/100.0f;
				}
			}
			else
			{
				apJoint->mfMin =0;
				apJoint->mfMax =0;
			}
		}

	}


	//-----------------------------------------------------------------------

	void cMeshLoaderCollada::CreateHierarchyNodes(cMesh *apMesh, cNode3D* mpParentNode,
													cColladaNode* apColladaNode,
													tColladaGeometryVec &avColladaGeom)
	{
		cNode3D* pNode = mpParentNode->CreateChild3D(apColladaNode->msName);
		apMesh->AddNode(pNode);

		pNode->SetMatrix(apColladaNode->m_mtxTransform);

		//Log("Node: %s\n",apColladaNode->msName.c_str());
		//Log(" local node transform: %s\n",pNode->GetLocalPosition().ToString().c_str());
		//Log(" local collada transform: %s\n",apColladaNode->m_mtxTransform.GetTranslation().ToString().c_str());
		//Log(" world transform: %s\n",apColladaNode->m_mtxWorldTransform.GetTranslation().ToString().c_str());

		pNode->SetPosition(pNode->GetLocalPosition());

		//Get source name
		if(apColladaNode->msSource!="")
		{
			for(int i=0;i<(int)avColladaGeom.size(); i++){
				if(avColladaGeom[i].msId == apColladaNode->msSource)
				{
					//Log("Setting source for %s to %s\n",pNode->GetName(), avColladaGeom[i].msName.c_str());
					pNode->SetSource(avColladaGeom[i].msName);
					break;
				}
			}
		}

		//Iterate children
		tColladaNodeListIt it = apColladaNode->mlstChildren.begin();
		for(; it != apColladaNode->mlstChildren.end(); ++it)
		{
			CreateHierarchyNodes(apMesh, pNode,*it,avColladaGeom);
		}
	}



	//-----------------------------------------------------------------------

	cColladaGeometry* cMeshLoaderCollada::GetGeometry(const tString& asId, tColladaGeometryVec &avGeomVec)
	{
		for(size_t i=0; i<avGeomVec.size(); i++)
		{
			if(avGeomVec[i].msId == asId)
			{
				return &avGeomVec[i];
			}
		}

		return NULL;
	}

	//-----------------------------------------------------------------------

	cColladaLight* cMeshLoaderCollada::GetLight(const tString& asId, tColladaLightVec &avLightVec)
	{
		for(size_t i=0; i<avLightVec.size(); i++)
		{
			if(avLightVec[i].msId == asId)
			{
				return &avLightVec[i];
			}
		}

		return NULL;
	}

	//-----------------------------------------------------------------------

	cMeshEntity* cMeshLoaderCollada::CreateStaticMeshEntity(cColladaNode* apNode, cWorld3D *apWorld,
												cColladaGeometry *apGeom, bool abInRoomGroup,
												tColladaMaterialVec &avColladaMaterials,
												tColladaTextureVec &avColladaTextures,
												tColladaImageVec &avColladaImages)
	{
		//Get the settings "in the name"
		tStringVec vParams;
		tString sSepp = "_";
		cString::GetStringVec(apNode->msName,vParams,&sSepp);

		cMeshEntity *pEntity = NULL;
		iVertexBuffer *pVtxBuffer = NULL;

		tString sMatName = GetMaterialTextureFile(apGeom->msMaterial,
												avColladaMaterials,avColladaTextures,
												avColladaImages);

		cMesh *pMesh = NULL;
		cSubMesh* pSubMesh = NULL;

		bool bDrawn = false;

		////////////////////////////////////////////
		// Check if the mesh is drawn
		if(HasParam(vParams, "nodraw")==false)
		{
			bDrawn = true;

			//Create mesh and sub mesh
			pMesh = hplNew( cMesh, (apNode->msName, mpMaterialManager,mpAnimationManager) );
			pSubMesh = pMesh->CreateSubMesh(apNode->msName+ "_Sub");

			//Create vertex buffer
			//tColladaExtraVtxListVec vExtraVtxVec;
			pVtxBuffer = CreateVertexBuffer(*apGeom, eVertexBufferUsageType_Static); ///,&apNode->m_mtxWorldTransform);

			//Check if If the mesh casts shadows:
			pVtxBuffer->CreateShadowDouble(true);

			//Transform vertex buffer with world transform
			pVtxBuffer->Transform(apNode->m_mtxWorldTransform);
			//Log("%s transform: (%s)\n", apNode->msName.c_str(), apNode->m_mtxWorldTransform.ToString().c_str());

			pSubMesh->SetVertexBuffer(pVtxBuffer);

			//Create triangle data for the geometry
			cMath::CreateTriangleData(*pSubMesh->GetTriangleVecPtr(),
				pVtxBuffer->GetIndices(), pVtxBuffer->GetIndexNum(),
				pVtxBuffer->GetArray(eVertexFlag_Position),
				kvVertexElements[cMath::Log2ToInt(eVertexFlag_Position)],
				pVtxBuffer->GetVertexNum());

			//Create edges for the geometry.
			bool bDoubleSided = false;
			cMath::CreateEdges(*pSubMesh->GetEdgeVecPtr(),
				pVtxBuffer->GetIndices(), pVtxBuffer->GetIndexNum(),
				pVtxBuffer->GetArray(eVertexFlag_Position),
				kvVertexElements[cMath::Log2ToInt(eVertexFlag_Position)],
				pVtxBuffer->GetVertexNum(),
				&bDoubleSided);

			pSubMesh->SetDoubleSided(bDoubleSided);

			pSubMesh->Compile();

			//Add material
			iMaterial *pMaterial;
			if(mbUseFastMaterial && msFastMaterialFile != "")
			{
				pMaterial = mpMaterialManager->CreateMaterial(msFastMaterialFile);
			}
			else
			{
				pMaterial = mpMaterialManager->CreateMaterial(sMatName);
			}
			if(pMaterial==NULL)
			{
				Error("Couldn't create material '%s' for object '%s'\n",sMatName.c_str(),
					apNode->msName.c_str());
				hplDelete(pMesh);
				return NULL;
			}

			pSubMesh->SetMaterial(pMaterial);

			//Create mesh entity
			pEntity = apWorld->CreateMeshEntity(apNode->msName, pMesh, false);
			pEntity->SetMatrix(cMatrixf::Identity);
			//pEntity->SetMatrix(apNode->m_mtxWorldTransform); //<- Debug!

			//Set the mesh as static.
			pEntity->SetStatic(true);
			pEntity->SetIsSaved(false);


			if(HasParam(vParams,"noshadow"))
				pEntity->SetCastsShadows(false);
			else
				pEntity->SetCastsShadows(true);
		}

		if(HasParam(vParams, "nocollide")==false)
		{
			if(bDrawn==false){
				pVtxBuffer = CreateVertexBuffer(*apGeom, eVertexBufferUsageType_Static);
				pVtxBuffer->Transform(apNode->m_mtxWorldTransform);
			}

			iCollideShape *pShape = apWorld->GetPhysicsWorld()->CreateMeshShape(pVtxBuffer);
			iPhysicsBody *pBody = apWorld->GetPhysicsWorld()->CreateBody(apNode->msName,pShape);

			if(pBody)
			{
				pBody->SetMass(0);
				pBody->SetIsSaved(false);
				//Log("Created body %s!\n",pBody->GetName().c_str());
			}
			else
			{
				Log("Body creation failed!\n");
			}

			//Check if it blocks light
			pBody->SetBlocksLight(true);
			if(bDrawn)
			{
				if(	pEntity->IsShadowCaster()==false ||
					(pSubMesh->GetMaterial() && pSubMesh->GetMaterial()->IsTransperant()))
				{
					pBody->SetBlocksLight(false);
				}
			}
			else
			{
				pBody->SetBlocksLight(false);
			}

			bool bBlocksSound = false;
			if(abInRoomGroup) bBlocksSound = true;

			if(HasParam(vParams,"nosoundblock")) bBlocksSound = false;
			if(HasParam(vParams,"soundblock")) bBlocksSound = true;

			pBody->SetBlocksSound(bBlocksSound);

			if(HasParam(vParams, "nocharcollide")) pBody->SetCollideCharacter(false);

			tString sPhysicsMatName = apWorld->GetResources()->GetMaterialManager()->GetPhysicsMaterialName(sMatName);
			iPhysicsMaterial *pPhysicsMat = apWorld->GetPhysicsWorld()->GetMaterialFromName(
																				sPhysicsMatName);
			if(pPhysicsMat)
			{
				pBody->SetMaterial(pPhysicsMat);
			}

			//Haptic creation
			if(cHaptic::GetIsUsed())
			{
				cHaptic	*pHaptic = apWorld->GetHaptic();
				iHapticShape *pHShape = NULL;

				pHShape = pHaptic->GetLowLevel()->CreateMeshShape(pBody->GetName(),pVtxBuffer);
				if(bDrawn) pHShape->SetSubMeshEntity(pEntity->GetSubMeshEntity(0));

				pBody->SetHapticShape(pHShape);

				if(pPhysicsMat)
				{
					iHapticSurface *pHapticSurface = pPhysicsMat->GetSurfaceData()->GetHapticSurface();
					if(pHapticSurface) pHShape->SetSurface(pHapticSurface);
				}
			}

			if(bDrawn==false){
				hplDelete(pVtxBuffer);
			}

		}

		return pEntity;
	}

	//-----------------------------------------------------------------------

	static iCollideShape* CreatePhysicsCollider(iPhysicsWorld* pPhysicsWorld,const cVector3f& avSize,
												eCollideShapeType aShapeType)
	{
		if(aShapeType == eCollideShapeType_Cylinder)
		{
			//This is to orient the cylinder along y axis instead of x.
			cMatrixf mtxOffset = cMath::MatrixRotateZ(cMath::ToRad(90));
			return pPhysicsWorld->CreateCylinderShape(avSize.x, avSize.y,&mtxOffset);
		}
		else if(aShapeType == eCollideShapeType_Capsule)
		{
			//This is to orient the cylinder along y axis instead of x.
			cMatrixf mtxOffset = cMath::MatrixRotateZ(cMath::ToRad(90));
			return pPhysicsWorld->CreateCapsuleShape(avSize.x, avSize.y,&mtxOffset);
		}
		else if(aShapeType == eCollideShapeType_Box)
		{
			return pPhysicsWorld->CreateBoxShape(avSize, NULL);
		}
		else if(aShapeType == eCollideShapeType_Sphere)
		{
			return pPhysicsWorld->CreateSphereShape(avSize.x,NULL);
		}

		return NULL;
	}

	cColliderEntity* cMeshLoaderCollada::CreateStaticCollider(cColladaNode* apNode, cWorld3D *apWorld,
															cColladaGeometry *apGeom,
															tColladaMaterialVec &avColladaMaterials,
															tColladaTextureVec &avColladaTextures,
															tColladaImageVec &avColladaImages,
															bool abCharacterCollider)
	{
		tStringVec vStrings;
		tString sSepp="_";
		cString::GetStringVec(apNode->msName,vStrings,&sSepp);

		tFloatVec vVertexVec;
		tVertexVec &vArray = apGeom->mvVertexVec;
		vVertexVec.resize(vArray.size() *3);

		for(size_t vtx=0; vtx < vArray.size(); ++vtx)
		{
			vVertexVec[vtx*3 + 0] = vArray[vtx].pos.x;
			vVertexVec[vtx*3 + 1] = vArray[vtx].pos.y;
			vVertexVec[vtx*3 + 2] = vArray[vtx].pos.z;
		}

		cBoundingVolume TempBV;
		TempBV.AddArrayPoints(&vVertexVec[0],(int)vArray.size());
		TempBV.CreateFromPoints(3);

		tString sShapeType = cString::ToLowerCase(vStrings[1]);
		eCollideShapeType ShapeType = eCollideShapeType_Box;
		cVector3f vShapeSize = TempBV.GetSize() * apNode->mvScale;

		if(sShapeType == "box"){
			ShapeType = eCollideShapeType_Box;
		}
		else if(sShapeType == "sphere"){
			ShapeType = eCollideShapeType_Sphere;
			vShapeSize *= cVector3f(0.5f);
		}
		else if(sShapeType == "capsule"){
			ShapeType = eCollideShapeType_Capsule;
			vShapeSize.x *= 0.5;
		}
		else if(sShapeType == "cylinder"){
			ShapeType = eCollideShapeType_Cylinder;
			vShapeSize.x *= 0.5;
		}

		iCollideShape *pCollideShape =NULL;

		pCollideShape = CreatePhysicsCollider(apWorld->GetPhysicsWorld(),vShapeSize,ShapeType);

		if(pCollideShape==NULL){
			Error("Collider was not created!");
			return NULL;
		}

		//Log("Creating Collider %s, type: %d size: %s\n", apGeom->msName.c_str(),(int)ShapeType,
		//												pCollideShape->GetSize().ToString().c_str());

		//Create body
		iPhysicsBody *pBody = apWorld->GetPhysicsWorld()->CreateBody(apNode->msName,pCollideShape);
		pBody->SetMatrix(apNode->m_mtxWorldTransform);

		//Set light block
		pBody->SetBlocksLight(false);

		//Set material
		tString sMatName = GetMaterialTextureFile(	apGeom->msMaterial,
													avColladaMaterials,avColladaTextures,
													avColladaImages);
		if(sMatName!="")
		{
			tString sPhysicsMatName = apWorld->GetResources()->GetMaterialManager()->GetPhysicsMaterialName(sMatName);

			if(sPhysicsMatName!="")
			{
				iPhysicsMaterial *pPhysicsMat = apWorld->GetPhysicsWorld()->GetMaterialFromName(
																			sPhysicsMatName);
				if(pPhysicsMat){
					pBody->SetMaterial(pPhysicsMat);
				}
			}
		}


		//Check if it blocks sound
		bool bBlocksSound = false;
		if(HasParam(vStrings,"soundblock")) bBlocksSound = true;

		pBody->SetBlocksSound(bBlocksSound);
		pBody->SetIsSaved(false);
		pBody->SetCollideCharacter(true);
		if(abCharacterCollider)
			pBody->SetCollide(false);
		else
			pBody->SetCollide(true);

		//Haptic creation
		if(cHaptic::GetIsUsed())
		{
			cHaptic	*pHaptic = apWorld->GetHaptic();
			iHapticShape *pHShape = pHaptic->GetLowLevel()->CreateShapeFromPhysicsBody(apNode->msName,pBody);
		}

		return apWorld->CreateColliderEntity(apNode->msName,pBody);
	}


	//-----------------------------------------------------------------------

	void cMeshLoaderCollada::AddSceneObjects(cColladaNode* apNode, cWorld3D *apWorld,
							tColladaGeometryVec &avColladaGeometries,
							tColladaLightVec &avColladaLights,
							tColladaMaterialVec &avColladaMaterials,
							tColladaTextureVec &avColladaTextures,
							tColladaImageVec &avColladaImages,
							cColladaScene *apColladaScene)
	{
		//////////////////////////////////////////////////
		//Check if we are dealing with a special type.
		if(apNode->msName.size() > 1 && apNode->msName[0] == '_')
		{
			tString sType = cString::Sub(apNode->msName,0,5);
			if(cString::ToLowerCase(sType) == "_room")
			{
				//Log("Found room, leaving branch...\n");
				return;
			}

			tStringVec vParams;
			tString sSepp = "_";

			cString::GetStringVec(apNode->msName,vParams,&sSepp);

			////////////////////////////////////////
			///// COLLIDER /////////////////////////////
			if(cString::ToLowerCase(vParams[0])=="collider")
			{
				cColladaGeometry* pGeom = GetGeometry(apNode->msSource, avColladaGeometries);
				if(pGeom)
				{
					cColliderEntity *pCollider = CreateStaticCollider(apNode,apWorld,pGeom,avColladaMaterials,
																		avColladaTextures, avColladaImages,
																		false);

					apWorld->GetPortalContainer()->Add(pCollider, true);
				}
				else
				{
					Error("Node '%s' does not have any geometry! Could not create collider!\n",apNode->msName.c_str());
				}
			}
			////////////////////////////////////////
			///// CHARACTER COLLIDER ///////////////
			else if(cString::ToLowerCase(vParams[0])=="charcollider")
			{
				cColladaGeometry* pGeom = GetGeometry(apNode->msSource, avColladaGeometries);
				if(pGeom)
				{
					cColliderEntity *pCollider = CreateStaticCollider(apNode,apWorld,pGeom,avColladaMaterials,
																		avColladaTextures, avColladaImages,
																		true);

					apWorld->GetPortalContainer()->Add(pCollider, true);
				}
				else
				{
					Error("Node '%s' does not have any geometry!C ould not create charcollider!\n",apNode->msName.c_str());
				}
			}
			////////////////////////////////////////
			///// SOUND /////////////////////////////
			else if(cString::ToLowerCase(vParams[0])=="sound" && !(mFlags & eWorldLoadFlag_NoEntities))
			{
				if(vParams.size() < 3){
					Error("Too few params in sound entity '%s'\n",apNode->msName.c_str());
				}
				else
				{
					tString sFile = "";
					for(size_t i=1; i<vParams.size()-1; ++i)
					{
						sFile += vParams[i];
						if(i != vParams.size()-2) sFile+="_";
					}
					tString sName = vParams[vParams.size()-1];

					cSoundEntity *pEntity = apWorld->CreateSoundEntity(sName,sFile,false);

					if(pEntity == NULL)
					{
						tString sName = vParams[1];
						tString sFile = cString::Sub(apNode->msName,7+(int)+sName.size()+1);

						pEntity = apWorld->CreateSoundEntity(sName,sFile,false);
					}
					if(pEntity) pEntity->SetMatrix(apNode->m_mtxWorldTransform);
				}
			}
			////////////////////////////////////////
			///// START /////////////////////////////
			else if(cString::ToLowerCase(vParams[0])=="start" && !(mFlags & eWorldLoadFlag_NoEntities))
			{
				if(vParams.size() < 2){
					Error("Too few params in start entity '%s'\n",apNode->msName.c_str());
				}
				else
				{
					tString sName = cString::Sub(apNode->msName,7);

					cStartPosEntity *pStart = apWorld->CreateStartPos(sName);
					if(pStart){
						pStart->SetMatrix(apNode->m_mtxWorldTransform);
					}
				}
			}
			////////////////////////////////////////
			///// REF /////////////////////////////
			else if(cString::ToLowerCase(vParams[0])=="ref")
			{
				if((mFlags & eWorldLoadFlag_NoGameEntities)) return;

				if(vParams.size() < 3){
					Error("Too few params in ref entity '%s'\n",apNode->msName.c_str());
				}
				else
				{
					tString sFile = "";
					for(size_t i=1; i<vParams.size()-1; ++i)
					{
						sFile += vParams[i];
						if(i != vParams.size()-2) sFile+="_";
					}
					tString sName = vParams[vParams.size()-1];

					iEntity3D* pEntity = apWorld->CreateEntity(sName,apNode->m_mtxWorldTransform,sFile, true);

					//If no entity was loaded, try old style.
					if(pEntity==NULL)
					{
						tString sName = vParams[1];
						tString sFile = cString::Sub(apNode->msName,5+(int)+sName.size()+1);
						sFile = cString::SetFileExt(sFile,"ent");

						apWorld->CreateEntity(sName,apNode->m_mtxWorldTransform,sFile, true);
					}
				}

				//Skip the children of the ref!
				return;
			}
			////////////////////////////////////////
			///// AI NODE //////////////////////////
			else if(cString::ToLowerCase(vParams[0])=="node")
			{
				if(vParams.size() < 3){
					Error("Too few params in ref entity '%s'\n",apNode->msName.c_str());
				}
				else
				{
					tString sType = vParams[1];
					tString sName = cString::Sub(apNode->msName,6+(int)sType.size()+1);

					apWorld->AddAINode(sName,sType,apNode->m_mtxWorldTransform.GetTranslation());
				}

				//Skip the children of the ref!
				return;
			}
			////////////////////////////////////////
			///// BILLBOARD /////////////////////////////
			else if(cString::ToLowerCase(vParams[0])=="bb" && !(mFlags & eWorldLoadFlag_NoEntities))
			{
				if(vParams.size() < 3){
					Error("Too few params in billboard entity entity '%s'\n",apNode->msName.c_str());
				}
				else
				{
					cVector2f vSize(apNode->mvScale.x,apNode->mvScale.y);
					float fOffset = apNode->mvScale.z;

					tString sName = vParams[vParams.size()-1];
					tString sFile = "";
					for(size_t i=1; i<vParams.size()-1; ++i)
					{
						sFile += vParams[i];
						if(i != vParams.size()-2) sFile+="_";
					}

					cBillboard *pBill = apWorld->CreateBillboard(sName,vSize);

					//To support old versions
					if(pBill == NULL)
					{
						sName = vParams[1];
						sFile = cString::Sub(apNode->msName,4+(int)sName.size()+1);

						pBill = apWorld->CreateBillboard(sName,vSize);
					}

					if(pBill)
					{
						pBill->SetForwardOffset(fOffset);

						//Old version , trying to skip because bounding box calc for this is far from
						//optimal
						//pBill->SetPosition(apNode->m_mtxWorldTransform.GetTranslation());
						//pBill->SetAxis(cMath::Vector3Normalize(cMath::MatrixInverse(apNode->m_mtxWorldTransform).GetUp()));

						//remove scale
						cMatrixf mtxScale = cMath::MatrixScale(apNode->mvScale);
						cMatrixf mtxWorld = cMath::MatrixMul(apNode->m_mtxWorldTransform, cMath::MatrixInverse(mtxScale));
						pBill->SetMatrix(mtxWorld);
						pBill->SetAxis(cVector3f(0,1,0));

						sFile = cString::SetFileExt(sFile,"bnt");
						pBill->LoadXMLProperties(sFile);
					}
				}
			}
			////////////////////////////////////////
			///// BEAM /////////////////////////////
			else if(cString::ToLowerCase(vParams[0])=="beam" && !(mFlags & eWorldLoadFlag_NoEntities))
			{
				if(vParams.size() < 3){
					Error("Too few params in billboard entity entity '%s'\n",apNode->msName.c_str());
				}
				else
				{
					cVector2f vSize(apNode->mvScale.x,apNode->mvScale.y);
					float fOffset = apNode->mvScale.z;

					tString sName = vParams[vParams.size()-1];
					tString sFile = "";
					for(size_t i=1; i<vParams.size()-1; ++i)
					{
						sFile += vParams[i];
						if(i != vParams.size()-2) sFile+="_";
					}

					//Find end point node
					tString sEndName = "_beamend_"+sName;
					cColladaNode *pEndNode = apColladaScene->GetNode(sEndName);

					if(pEndNode)
					{
						cBeam *pBeam = apWorld->CreateBeam(sName);

						if(pBeam)
						{
							pBeam->SetPosition(apNode->m_mtxWorldTransform.GetTranslation());
							pBeam->GetEnd()->SetPosition(pEndNode->m_mtxWorldTransform.GetTranslation());

							sFile = cString::SetFileExt(sFile,"beam");
							pBeam->LoadXMLProperties(sFile);
						}
					}
					else
					{
						Error("Couldn't find beam end '%s'!\n",sEndName.c_str());
					}
				}

			}
			////////////////////////////////////////
			///// PARTICLE SYSTEM //////////////////
			else if(cString::ToLowerCase(vParams[0])=="ps" && !(mFlags & eWorldLoadFlag_NoEntities))
			{
				if(vParams.size() < 3){
					Error("Too few params in billboard entity entity '%s'\n",apNode->msName.c_str());
				}
				else
				{
					tString sName = vParams[vParams.size()-1];
					tString sType = "";
					for(size_t i=1; i<vParams.size()-1; ++i)
					{
						sType += vParams[i];
						if(i != vParams.size()-2) sType+="_";
					}

					cParticleSystem3D *pPS = apWorld->CreateParticleSystem(sName,sType,apNode->mvScale,
																		apNode->m_mtxWorldTransform);

					if(pPS == NULL)
					{
						sName = vParams[1];
						sType = cString::Sub(apNode->msName,4+(int)sName.size()+1);
						cParticleSystem3D *pPS = apWorld->CreateParticleSystem(sName,sType,apNode->mvScale,
																				apNode->m_mtxWorldTransform);

						if(pPS == NULL)
						{
							Error("Couldn't load particle system '%s' with type '%s'\n",
														sName.c_str(), sType.c_str());
						}
					}

				}
			}
			////////////////////////////////////////
			///// AREA /////////////////////////////
			if(cString::ToLowerCase(vParams[0])=="area" && !(mFlags & eWorldLoadFlag_NoEntities))
			{
				if(vParams.size() < 2){
					Error("Too few params in area entity '%s'\n",apNode->msName.c_str());
				}
				else
				{
					cColladaGeometry* pGeom = GetGeometry(apNode->msSource, avColladaGeometries);

					tFloatVec vVertexVec;
					tVertexVec &vArray = pGeom->mvVertexVec;
					vVertexVec.resize(vArray.size() *3);

					for(size_t vtx=0; vtx < vArray.size(); ++vtx)
					{
						vVertexVec[vtx*3 + 0] = vArray[vtx].pos.x;
						vVertexVec[vtx*3 + 1] = vArray[vtx].pos.y;
						vVertexVec[vtx*3 + 2] = vArray[vtx].pos.z;
					}

					cBoundingVolume TempBV;
					TempBV.AddArrayPoints(&vVertexVec[0],(int)vArray.size());
					TempBV.CreateFromPoints(3);

					tString sType;
					tString sName;
					if(vParams.size() < 3 || (mFlags & eWorldLoadFlag_NoGameEntities))
					{
						sType = "";
						if(vParams.size() < 3)
							sName = cString::ToLowerCase(vParams[1]);
						else
							sName = cString::Sub(apNode->msName,(int)vParams[0].size()+(int)vParams[1].size()+3);
					}
					else
					{
						sType = cString::ToLowerCase(vParams[1]);
						sName = cString::Sub(apNode->msName,(int)vParams[0].size()+(int)vParams[1].size()+3);
					}

					cVector3f vSize = TempBV.GetSize() * apNode->mvScale;

					if(sType!="")
					{
						iArea3DLoader *pLoader = apWorld->GetResources()->GetArea3DLoader(sType);
						if(pLoader)
						{
							pLoader->Load(sName,vSize,apNode->m_mtxWorldTransform,apWorld);
						}
					}

					//Create engine area.
					cAreaEntity *pArea = apWorld->CreateAreaEntity(sName);
					pArea->m_mtxTransform = apNode->m_mtxWorldTransform;
					pArea->msType = sType;
					pArea->mvSize = vSize;
				}
			}

		}
		//////////////////////////////////////////////////
		//Load light or geometry
		else
		{
			//The node has a source
			if(apNode->msSource != "")
			{
				//Get number of chars in prefix.
				int lPrefixChars =1;
				while(	lPrefixChars < apNode->msName.size() &&
						apNode->msName[lPrefixChars]!= '_' &&
						apNode->msName[lPrefixChars]!='\0') {
						lPrefixChars++;
					}

				//Get prefix
				tString sPrefix = cString::ToLowerCase(cString::Sub(apNode->msName,0,lPrefixChars));

				/////////////////////////////////////////////////
				// Load source from external file
				if(apNode->mbSourceIsFile)
				{
					tString sFile = cString::SetFileExt(cString::GetFileName(apNode->msSource),"");

					//If static, load mesh from mesh file.
					if(mFlags & eWorldLoadFlag_NoGameEntities)
					{
						//Do nothing...
					}
					else if(sPrefix == "static")
					{
						cMesh *pMesh = mpMeshManager->CreateMesh(sFile);
						if(pMesh)
						{
							//Create the entity
							cMeshEntity* pEntity =  apWorld->CreateMeshEntity(apNode->msName,pMesh, false);

							//Log("Static mesh entity mtx: %s\n",cMath::MatrixToChar(apNode->m_mtxWorldTransform));
							pEntity->SetMatrix(apNode->m_mtxWorldTransform);

							apWorld->GetPortalContainer()->Add(pEntity, true);

							//Set parameters
							tStringVec vParams;
							tString sSepp = "_";
							cString::GetStringVec(apNode->msName,vParams,&sSepp);

							if(HasParam(vParams,"noshadow"))
								pEntity->SetCastsShadows(false);
							else
								pEntity->SetCastsShadows(true);
						}
					}
					//If it is not static it is an entity. Load form entity file.
					else
					{
						tString sEntityFile = cString::SetFileExt(sFile,"ent");

						apWorld->CreateEntity(apNode->msName,apNode->m_mtxWorldTransform,sEntityFile, true);
					}

				}
				///////////////////////////////////////
				// Load source from collada data
				else
				{
					//Create an entity from the geometry.
					cColladaGeometry* pGeom = GetGeometry(apNode->msSource, avColladaGeometries);
					if(pGeom)
					{
						//Log("Creating mesh '%s'!\n",pGeom->msName.c_str());

						cMeshEntity *pEntity = CreateStaticMeshEntity(apNode, apWorld,
							pGeom,false,
							avColladaMaterials,
							avColladaTextures, avColladaImages);

						if(pEntity)
						{
							apWorld->GetPortalContainer()->Add(pEntity, true);
						}
					}
					else if(!(mFlags & eWorldLoadFlag_NoLights))
					{
						//If there wasn't any geometry, try and find a light.
						cColladaLight* pColladaLight = GetLight(apNode->msSource, avColladaLights);
						if(pColladaLight)
						{
							tStringVec vParams;
							tString sSepp = "_";
							cString::GetStringVec(apNode->msName,vParams,&sSepp);

							//check if this is an dynamic light
							bool bStatic = true;
							int lParamAdd =0; //Too make it easier to support dynamic param
							if(cString::ToLowerCase(vParams[0]) == "dynamic"){
								bStatic =false;
								lParamAdd =1;
							}

							tString sLightName ="";
							tString sLightFile ="";

							//Check if light might have file parameter
							if((int)vParams.size() >= 2 +lParamAdd)
							{
								for(size_t i=lParamAdd; i< vParams.size()-1; ++i)
								{
									sLightFile += vParams[i];
									if(i!= vParams.size()-2) sLightFile += "_";
								}
								sLightFile = cString::SetFileExt(sLightFile,"lnt");

								sLightName = vParams[vParams.size()-1];
							}
							//No file parameter
							else
							{
								sLightName = vParams[lParamAdd];
							}


							/////////////////////////////////////
							//Load the specific light type
							if(pColladaLight->msType =="point")
							{
								cLight3DPoint *pLight = apWorld->CreateLightPoint(sLightName, false);

								pLight->SetMatrix(apNode->m_mtxWorldTransform);
								pColladaLight->mDiffuseColor.a = apNode->mvScale.y;
								pLight->SetDiffuseColor(pColladaLight->mDiffuseColor);
								pLight->SetFarAttenuation(apNode->mvScale.x);

								if(apNode->mvScale.z < 0.1f)
									pLight->SetCastShadows(false);
								else
									pLight->SetCastShadows(true);


								if(bStatic) pLight->SetStatic(bStatic);

								if(mbRestricStaticLightToSector) pLight->SetOnlyAffectInSector(true);

								apWorld->GetPortalContainer()->Add(pLight, bStatic);

								//Log("Added light '%s' attenuation:  %f a: %f\n",pLight->GetName().c_str(),
								//		pLight->GetFarAttenuation(), pLight->GetDiffuseColor().a);

								if(sLightFile != "")
									pLight->LoadXMLProperties(sLightFile);
							}
							else if(pColladaLight->msType =="spot")
							{
								cLight3DSpot *pLight = apWorld->CreateLightSpot(sLightName,"", false);

								pLight->SetMatrix(apNode->m_mtxWorldTransform);
								pLight->SetDiffuseColor(pColladaLight->mDiffuseColor);
								pLight->SetFarAttenuation(apNode->mvScale.x);
								pLight->SetFOV(cMath::ToRad(pColladaLight->mfAngle));

								if(bStatic) pLight->SetStatic(bStatic);

								if(mbRestricStaticLightToSector) pLight->SetOnlyAffectInSector(true);

								apWorld->GetPortalContainer()->Add(pLight, bStatic);

								if(sLightFile != "")
									pLight->LoadXMLProperties(sLightFile);
							}
							else
							{
								Warning("Invalid light type '%s' for light '%s'\n",pColladaLight->msType.c_str(),
																	apNode->msName.c_str());
							}
						}
						else
						{
							Warning("Source '%s' is not found!\n", apNode->msSource.c_str());
						}
					}
				}
			}
		}

		//Iterate children.
		tColladaNodeListIt ChildIt = apNode->mlstChildren.begin();
		for(; ChildIt != apNode->mlstChildren.end();ChildIt++)
		{
			AddSceneObjects(*ChildIt, apWorld,avColladaGeometries,avColladaLights,
				avColladaMaterials,avColladaTextures,avColladaImages,apColladaScene);
		}
	}

	//-----------------------------------------------------------------------

	void cMeshLoaderCollada::AddSectorChildren(cColladaNode* apNode, tString asSector, cWorld3D *apWorld,
												tColladaGeometryVec &avColladaGeometries,
												tColladaLightVec &avColladaLights,
												tColladaMaterialVec &avColladaMaterials,
												tColladaTextureVec &avColladaTextures,
												tColladaImageVec &avColladaImages)
	{
		//Log("--- Node: %s\n",apNode->msName.c_str());

		//////////////////////////////////////////////////
		//Check if we are dealing with a special type.
		if(apNode->msName[0] == '_')
		{
			//Check if it is a portal
			tString sType = cString::Sub(apNode->msName,0,7);
			if(cString::ToLowerCase(sType) == "_portal")
			{
				////////////////////////////////
				//Get portal number

				//Get digits in num:
				int lDigits =1;
				while(apNode->msName[7+lDigits] != '_' && apNode->msName[7+lDigits] != 0) lDigits++;

				//get string and convert to int
				tString sNum = cString::Sub(apNode->msName,7,lDigits);
				int lNum = cString::ToInt(sNum.c_str(),-1);
				if(lNum==-1){
					Warning("Bad portal name: '%s'!\n",apNode->msName.c_str());
				}

				///////////////////////////////////////////
				// Get target room

				//Get the char pos for the target room
				int lStartChar = 7 + lDigits + 1;
				tString sTest = cString::ToLowerCase(cString::Sub(apNode->msName,lStartChar, 4));
				if(sTest != "room"){
					Error("Bad portal id 's'!\n", apNode->msName.c_str());
					return;
				}

				//Get number of digits
				lDigits =1;
				while(	apNode->msName.length() > lStartChar+4+lDigits &&
						apNode->msName[lStartChar+4+lDigits] != '_' &&
						apNode->msName.length() >= 7+lDigits &&
						apNode->msName[7+lDigits] != 0)
				{
					lDigits++;
				}

				sNum = cString::Sub(apNode->msName,lStartChar+4,lDigits);
				//int lTargetSector = cString::ToInt(sNum.c_str(),-1);
				tString sTargetSector = sNum;

				////////////////////////////////////////////
				// Get the portals in the target room it can see.

				lStartChar = lStartChar+4+lDigits;
				tIntVec vPortalIds;

				tString sValueString =  cString::Sub(apNode->msName,lStartChar);
				tString sSepp = "_";
				cString::GetIntVec(sValueString, vPortalIds,&sSepp);

				cColladaGeometry* pGeom = GetGeometry(apNode->msSource, avColladaGeometries);
				if(pGeom)
				{
					cPortal *pPortal = hplNew( cPortal, (lNum, apWorld->GetPortalContainer()) );

					//Set target
					pPortal->SetTargetSector(sTargetSector);

					//Add portals that can be seen.
					for(size_t i=0; i< vPortalIds.size();i++)
					{
						pPortal->AddPortalId(vPortalIds[i]);
						//Log("Seeing: %d\n", vPortalIds[i]);
					}

					/////////////////////////////////////
					//Calculate the normal of the portal
					cVector3f vNormal = cVector3f(0,0,0);
					for(size_t i=0; i < pGeom->mvVertexVec.size(); i++)
					{
						vNormal += pGeom->mvVertexVec[i].norm;
					}
					vNormal = cMath::MatrixMul(apNode->m_mtxWorldTransform.GetRotation(), vNormal);
					vNormal.Normalise();
					pPortal->SetNormal(vNormal);

					/////////////////////////////////
					//Add the points
					for(size_t i=0; i < pGeom->mvVertexVec.size(); i++)
					{
						cVector3f vPos = cMath::MatrixMul(apNode->m_mtxWorldTransform,
															pGeom->mvVertexVec[i].pos);
						pPortal->AddPoint(vPos);
					}

					//////////////////////////
					//Set the world transform.
					pPortal->SetTransform(cMatrixf::Identity);//apNode->m_mtxWorldTransform);

					/////////////////////
					//Compile the portal
					pPortal->Compile();

					////////////////////////
					//Add portal to sector.
					apWorld->GetPortalContainer()->AddPortal(pPortal, asSector);
				}
			}
		}
		////////////////////////////////////////////
		// Add normal geometry or light
		else
		{
			if(apNode->msSource != "")
			{
				if(apNode->mbSourceIsFile)
				{
					Error("Entities are NOT allowed in the room tree!\n");
				}
				else
				{
					//Create an entity from the geometry.
					cColladaGeometry* pGeom = GetGeometry(apNode->msSource, avColladaGeometries);
					if(pGeom)
					{
						//Log("Creating mesh '%s' from source '%s'!\n",pGeom->msName.c_str(),apNode->msSource.c_str());

						cMeshEntity *pEntity = CreateStaticMeshEntity(apNode, apWorld,
													pGeom, true,
													avColladaMaterials,
													avColladaTextures, avColladaImages);

						if(pEntity)
						{
							apWorld->GetPortalContainer()->AddToSector(pEntity, asSector);
						}
					}
					else
					{
						cColladaLight* pLight = GetLight(apNode->msSource, avColladaLights);
						if(pLight)
						{
							Error("Lights are NOT allowed in the room tree!\n");
						}
						else
						{
							Warning("Source '%s' is not found!\n", apNode->msSource.c_str());
						}
					}
				}
			}
		}

		//Log("---\n");

		//////////////////////////////////////////////////
		//Iterate children.
		tColladaNodeListIt ChildIt = apNode->mlstChildren.begin();
		for(; ChildIt != apNode->mlstChildren.end();ChildIt++)
		{
			AddSectorChildren(*ChildIt, asSector, apWorld,avColladaGeometries,avColladaLights,
				avColladaMaterials,avColladaTextures,avColladaImages);
		}
	}

	//-----------------------------------------------------------------------

}
